home *** CD-ROM | disk | FTP | other *** search
- ///-*-C++-*-//////////////////////////////////////////////////////////////////
- //
- // Hoard: A Fast, Scalable, and Memory-Efficient Allocator
- // for Shared-Memory Multiprocessors
- // Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
- //
- // Copyright (c) 1998-2000, The University of Texas at Austin.
- //
- // This library is free software; you can redistribute it and/or modify
- // it under the terms of the GNU Library General Public License as
- // published by the Free Software Foundation, http://www.fsf.org.
- //
- // This library is distributed in the hope that it will be useful, but
- // WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // Library General Public License for more details.
- //
- //////////////////////////////////////////////////////////////////////////////
-
- #ifndef _LOG_H_
- #define _LOG_H_
-
- #include <assert.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <pthread.h>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/mman.h>
-
- #define USE_LOCKS 0
-
- #if USE_LOCKS
-
- class lock {
- public:
- lock (pthread_mutex_t& l)
- : _theLock (l)
- {
- pthread_mutex_lock (&_theLock);
- }
-
- ~lock (void)
- {
- pthread_mutex_unlock (&_theLock);
- }
- private:
- pthread_mutex_t& _theLock;
- };
-
- #else
-
- class lock {
- public:
- lock (pthread_mutex_t&)
- {}
-
- ~lock (void)
- {}
- };
-
- #endif // USE_LOCKS
-
-
- // A log that can be treated as a dynamic array and a file.
-
- template <class TYPE>
- class Log {
- public:
-
- enum { READ_WRITE = 0,
- READ_ONLY = 1 };
-
- inline Log (void);
- ~Log (void) {
- // Close the logfile if it's still open.
- if (isOpen())
- close();
- }
-
- // Open a (possibly new) log.
- void open (const char * filename,
- int readOnly = READ_WRITE)
- {
- lock m (_lock);
- _readOnlyMode = readOnly;
- unlocked_open (filename);
- }
-
- // Close a log.
- inline void close (void);
-
- // Abort a log. (Don't write any changes.)
- inline void abort (void);
-
- // Clear a log.
- inline void clear (void);
-
- // Access a log entry directly.
- inline TYPE& operator[] (int i);
- inline TYPE operator[] (int i) const;
-
- // Add an entry to the end of the log.
- void append (TYPE& t) {
- assert (!isReadOnly());
- lock m (_lock);
- int len = _perceivedLength;
- adjustArray (len);
- assert (_perceivedLength == len + 1);
- ((TYPE *) ((char *) _start + sizeof(int)))[len] = t;
- }
-
- // Return the number of elements in the log.
- int length (void) {
- lock m (_lock);
- assert (_isOpen);
- int len = _perceivedLength;
- return len;
- }
-
- private:
-
- // Open a file and mmap it. This version does not acquire the lock.
- inline void unlocked_open (const char * filename);
-
- // Close the file and related memory maps.
- void closeEverything (void) {
- if (!isReadOnly()) {
- // Save the perceived length.
- *((int *) _start) = _perceivedLength;
-
- // Mmap the file and extend it as needed.
- int fd = ::open (_filename, O_CREAT | O_RDWR, 00600);
- assert (fd != -1);
- lseek (fd, fileLength(_perceivedLength) - 1, SEEK_SET);
- int x = 0;
- write (fd, (void *) &x, 1);
- char * fileStart = (char *) mmap (0, fileLength(_perceivedLength), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- memcpy (fileStart, _start, fileLength(_perceivedLength));
- // Make sure everything has been written out, then close the file.
- msync (fileStart, fileLength(_perceivedLength), MS_SYNC);
- munmap (fileStart, fileLength(_perceivedLength));
- }
- // Close the memory mapped block as well.
- munmap (_start, fileLength(_actualLength));
- ::close (_memFd);
- }
-
- // Grow the array if necessary to accomodate the given index.
- inline void adjustArray (int index);
-
- int isOpen (void) {
- return _isOpen;
- }
-
- int isReadOnly (void) {
- return (_readOnlyMode == READ_ONLY);
- }
-
- // Given the total number of objects, return the file length.
- int fileLength (int nobjs) {
- return nobjs * sizeof(TYPE) + sizeof(int);
- }
-
- // Given the length of a file, return the total number of objects.
- int numberOfObjects (int fileLen) {
- return ((int) fileLen - sizeof(int)) / sizeof(TYPE);
- }
-
- char _filename[255]; // The log's filename.
- char * _start; // The start address of the mmapped memory block.
- int _actualLength; // The actual length of the file, in # of TYPEs.
- int _perceivedLength; // The perceived length of the file, in # of TYPEs.
- int _memFd; // The file descriptor of the mmapped memory block.
- int _isOpen; // Is the log file open?
- int _readOnlyMode; // Did we open the file read-only?
- pthread_mutex_t _lock;
-
- double _pad[8];
-
- };
-
-
- /*
-
- The structure of the log file is as follows:
-
- +------------------+------+------+------+------+------+
- | perceived length | item | item | item | item | item |
- +------------------+------+------+------+------+------+
-
- The logfile implicitly grows to fit as the 'array' is indexed
- (or append is called). We always double the actual size of the
- array so we only do a logarithmic number of array re-sizes.
-
- */
-
-
- template <class TYPE>
- Log<TYPE>::Log (void)
- : _start (NULL),
- _actualLength (0),
- _perceivedLength (0),
- _isOpen (0)
- {}
-
-
- template <class TYPE>
- void Log<TYPE>::unlocked_open (const char * filename)
- {
- if (!isOpen()) {
-
- // Save the filename.
- strncpy ((char *) _filename, (char *) filename, 255);
-
- int nobjs;
- if (!isReadOnly()) {
- // Open the file, creating it if necessary.
- int fd = ::open (_filename, O_CREAT | O_RDWR, 00600);
- assert (fd != -1);
-
- // Find out how long the file is.
- struct stat buf;
- fstat (fd, &buf);
-
- // Calculate how many objects are in the file.
- if (buf.st_size == 0) {
- // The file is empty.
- nobjs = 0;
- // Write a zero into the beginning for the perceived length.
- int pl = 0;
- write (fd, (void *) &pl, sizeof(int));
- } else {
- nobjs = numberOfObjects((int) buf.st_size);
- assert (fileLength(nobjs) == (int) buf.st_size);
- }
-
- // Copy the file into memory.
-
- // mmap the file.
- char * fileStart = (char *) mmap (0, fileLength(nobjs), PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
-
- _memFd = ::open ("/dev/zero", O_RDWR);
- _start = (char *) mmap (0, fileLength(nobjs), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, _memFd, 0);
- memcpy (_start, fileStart, fileLength(nobjs));
- munmap (fileStart, fileLength(nobjs));
- ::close (fd);
-
- _isOpen = 1;
-
- } else {
-
- // Read-only.
-
- // Open the file.
- // The file MUST exist and be non-empty.
- _memFd = ::open (_filename, O_RDONLY, 00600);
- assert (_memFd != -1);
-
- // Find out how long the file is.
- struct stat buf;
- fstat (_memFd, &buf);
-
- // Calculate how many objects are in the file.
- nobjs = numberOfObjects((int) buf.st_size);
- assert (fileLength(nobjs) == (int) buf.st_size);
-
- // mmap the file.
- _start = (char *) mmap (0, fileLength(nobjs), PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _memFd, 0);
-
- _isOpen = 1;
- }
-
-
- // Make sure the mmap worked.
- assert ((int) _start != -1);
- assert (_start != NULL);
-
- // Set the length values.
- _actualLength = nobjs;
- _perceivedLength = *((int *) _start);
-
- // If there are no objects, the perceived length better be zero.
- assert ((nobjs != 0) || (_perceivedLength == 0));
- // Other sanity checks.
- assert (_perceivedLength <= _actualLength);
- assert (_actualLength >= 0);
- assert (_perceivedLength >= 0);
- }
- }
-
-
- template <class TYPE>
- void Log<TYPE>::close (void) {
- lock m (_lock);
- if (isOpen()) {
- assert (_start != NULL);
- closeEverything();
- _isOpen = 0;
- _actualLength = 0;
- _perceivedLength = 0;
- }
- }
-
-
- template <class TYPE>
- void Log<TYPE>::abort (void) {
- lock m (_lock);
- if (isOpen()) {
- assert (_start != NULL);
- munmap (_start, fileLength(_actualLength));
- ::close (_memFd);
- _isOpen = 0;
- _actualLength = 0;
- _perceivedLength = 0;
- }
- }
-
-
- template <class TYPE>
- void Log<TYPE>::clear (void) {
- lock m (_lock);
- if (isOpen()) {
- // Close the file & the mmap.
- closeEverything();
- _isOpen = 0;
- // Now delete it and re-open it.
- unlink (_filename);
- unlocked_open (_filename);
- }
- }
-
-
- template <class TYPE>
- TYPE& Log<TYPE>::operator[] (int i)
- {
- lock m (_lock);
- if (!isReadOnly()) {
- // Increase the array length to accomodate index i, if necessary.
- adjustArray (i);
- }
- // Return a reference to the chosen array entry.
- return ((TYPE *) ((char *) _start + sizeof(int)))[i];
- }
-
-
- template <class TYPE>
- void Log<TYPE>::adjustArray (int i)
- {
- assert (isOpen());
- assert (!isReadOnly());
- assert (i >= 0);
- // If we try to access an object that is beyond the actual length of
- // the file, we need to extend the file and update the mmap.
- if (i >= _actualLength) {
- int newMemFd = ::open ("/dev/zero", O_RDWR);
- char * newStart = (char *) mmap (0, fileLength(2 * i + 1), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, newMemFd, 0);
- memcpy (newStart, _start, fileLength(_actualLength));
- ::close (_memFd);
- _start = newStart;
- _memFd = newMemFd;
- _actualLength = 2 * i + 1;
- }
- assert (i < _actualLength);
- // At this point, i definitely fits in the space allocated to it.
- // However, we may need to extend the perceived length.
- if (i >= _perceivedLength) {
- _perceivedLength = i + 1;
- }
- assert (i < _perceivedLength);
- }
-
-
- template <class TYPE>
- TYPE Log<TYPE>::operator[] (int i) const
- {
- // Here we do NOT update the array size. The index should be within
- // the perceived size of the array.
- lock m (_lock);
- assert (isOpen());
- assert (i >= 0);
- assert (i < _perceivedLength);
- return ((TYPE *) ((char *) _start + sizeof(int)))[i];
- }
-
-
- #endif // _LOG_H_
-